package nachos.threads;

import nachos.machine.*;

import java.util.LinkedList;

/**
 * 通过关中断进行同步的条件变量的实现
 *
 * <p>
 * 你必须实现这个
 *
 * @see nachos.threads.Condition
 */
public class Condition2 {
    /**
     * 分配一个新的条件变量
     *
     * @param conditionLock 与此条件变量关联的锁
     *                      当前线程在使用 sleep()、wake() 和 wakeAll() 时必须持有该锁
     */
    public Condition2(Lock conditionLock) {
        this.conditionLock = conditionLock;

        waitThreadList = new LinkedList<>();
    }

    /**
     * 自动释放关联的锁，并在此条件变量上进入休眠状态，直到另一个线程使用 wake() 将它唤醒
     * 当前线程必须持有关联的锁
     * 线程将在 sleep() 返回之前自动重新获取锁
     */
    public void sleep() {
        Lib.assertTrue(conditionLock.isHeldByCurrentThread());

        conditionLock.release();

        // 关中断，并获取之前的中断状态
        boolean intStatus = Machine.interrupt().disable();

        // 将当前线程添加到等待线程列表
        waitThreadList.add(KThread.currentThread());
        KThread.currentThread().sleep();

        // 恢复中断
        Machine.interrupt().restore(intStatus);

        conditionLock.acquire();
    }

    /**
     * 最多唤醒一个在此条件变量上休眠的线程
     * 当前线程必须持有关联的锁
     */
    public void wake() {
        Lib.assertTrue(conditionLock.isHeldByCurrentThread());

        // 关中断，并获取之前的中断状态
        boolean intStatus = Machine.interrupt().disable();

        // 如果等待线程列表非空
        if (!waitThreadList.isEmpty()) {
            // 获取等待线程队列的第一个线程
            KThread thread = waitThreadList.removeFirst();
            if (thread != null) {
                // 让它进入就绪状态
                thread.ready();
            }
        }

        // 恢复中断
        Machine.interrupt().restore(intStatus);
    }

    /**
     * 唤醒在此条件变量上睡眠的所有线程
     * 当前线程必须持有关联的锁
     */
    public void wakeAll() {
        Lib.assertTrue(conditionLock.isHeldByCurrentThread());

        // 关中断，并获取之前的中断状态
        boolean intStatus = Machine.interrupt().disable();

        // 遍历条件变量中所有等待的线程
        for (KThread thread : waitThreadList) {
            // 让这些线程进入就绪状态
            thread.ready();
        }

        // 清空等待列表
        waitThreadList.clear();

        // 恢复中断
        Machine.interrupt().restore(intStatus);
    }

    private Lock conditionLock;
    private LinkedList<KThread> waitThreadList;
}
